
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;

namespace Swaf.Container
{
	public enum XmlSerializationSupport {None, AsAttribute, AsElement};

	/// <summary>
	/// Classes should implememnt this interface to show support for getting/setting values based on
	/// the namespace address formatting.
	/// </summary>
	/// <remarks>
	/// A namespace address is a '.' delimited list of names in a string that indicates a property to 
	/// get or set deep within an object tree contained within the object implementing this interface.  
	/// Examples of a namespace address include:
	/// <ul>
	/// <li>"street" - A single address within the namespace address.  The object implemting this
	/// interface would need to get/put the "street" property directly.</li>
	/// <li>"address.street" - This object would need to get/put a property called "address",
	/// and call get/put on that property for the "street" part of the namespace address.</li>
	/// <li>"user.address.street" - This object would need to get/put on "user", then call get/put on
	/// the "user" property with "address.street".</li>
	/// </ul>
	/// </remarks>
	public interface INameAddressSupport : IEnumerable
	{
		/// <summary>Gets the value of the specified field from <code>this BusinessObject</code>,
		/// or <code>null</code> if there is no such field.</summary>
		/// <remarks>
		/// <p>A call of the form <code>obj.get("a.b",value)</code> is equivalent to
		/// <code>obj.get("a").get("b")</code> (ignoring the typecast that is
		/// needed to make the latter compile cleanly).  Any number of dots are
		/// allowed; e.g.,
		/// <code>aPatient.get("PrimaryCarePhysician.Father.Address.Street1")</code>
		/// gets a patient's doctor's father's address's street.
		/// It returns <code>null</code> if any name in the chain is invalid.</p>
		/// </remarks>
		/// <param name="name">The name of the field whose value is being retrieved.</param>
		/// <returns>Returns the current value of the indicated field.  Returns <code>null</code>
		/// if there is no such field in <code>this</code> <code>BusinessObject</code> (that is, if
		/// <code>this.containsField(name)</code> returns <code>false</code>).
		/// <p><em>Note:</em> Since <code>put()</code> is allowed to change fields to
		/// values that may be invalid and/or of the wrong type, there is no guarantee
		/// that the return value will be valid, or even of the same type as the
		/// field's type.  For example, if the underlying type of <code>this</code>
		/// object's <code>Age</code> field is <code>Integer</code>, someone could legally say,
		/// <code>this.put("Age", "not-an-integer")</code>, in which case
		/// <code>this.get("Age")</code> would return <code>"not-an-integer"</code>.
		/// </p><p>
		/// However <em>after</em> the next successful call to <code>this.validate()</code>
		/// or <code>putValidated()</code>, this method is guaranteed to return a valid field of
		/// the right type.  (Of course it will still return <code>null</code> if <code>this.contains(name)</code>
		/// returns <code>false</code>; that is, if <code>name</code> is an inappropriate field name for
		/// <code>this</code> <code>BusinessObject</code>'s type.)
		/// </p></returns>
		object get(string name);
		object get(string name, object oneArg);
		object get(string name, params object[] p);

		/// <summary>
		/// Changes the value of the specified field of <code>this</code> <code>BusinessObject</code>,
		/// delaying all validity checks until the next call to <code>validate()</code>.</summary>
		/// <remarks>
		/// <p><b>Precondition:</b> <code>name</code> must be a field of <code>this</code>
		/// <code>BusinessObject</code>.  That is, <code>this.containsField(name)</code> must
		/// return <code>true</code>.  If not, logs a message in the error log and returns silently.
		/// </p><p>
		/// A call of the form <code>obj.put("a.b",value)</code> is equivalent to
		/// <code>obj.get("a").put("b",value)</code> (ignoring the typecast that is
		/// needed to make the latter compile cleanly).  Any number of dots are
		/// allowed; e.g.,
		/// <code>aPatient.put("PrimaryCarePhysician.Father.Address.Street1",value)</code>
		/// sets a patient's doctor's father's address's street.
		/// If any name in the chain is invalid, it logs a message to the error log and returns silently.
		/// </p><p>
		/// <b>Deferred Preconditions:</b> <em>Important:</em> these preconditions
		/// are <em>not</em> checked until you call <code>this.validate()</code>.  See
		/// <code>putValidated()</code> if you want these checks to occur immediately:
		/// <ul>
		/// <li><code>value</code> must not be <code>null</code>.</li>
		/// 
		/// <li>the type of <code>value</code> must either be what is expected for the
		/// specified field, or <code>String</code>.  For example, if <code>this</code>
		/// <code>BusinessObject</code>'s <code>Age</code> field is supposed to be of type
		/// <code>Integer</code>, <code>value</code> must either be an <code>Integer</code> or a
		/// <code>String</code>.  It must not be, for example, a <code>IList</code>.</li>
		/// 
		/// <li>if <code>value</code> is of type <code>String</code>, it must be valid according
		/// to the field's "validator."  For example, if there is a <code>ZipCode</code>
		/// field in the XML that defines <code>this</code> <code>BusinessObject</code>,
		/// and if that field's <code>validator=</code> specification contains
		/// <code>"zip5+opt4"</code>, then calling <code>this.put("ZipCode","xyzzy")</code>
		/// will be illegal (assuming the <code>zip5+opt4</code> validator rejects the
		/// string <code>"xyzzy"</code>).</li>
		/// 
		/// <li>if the type of <code>value</code> is supposed to be some aggregate type
		/// (such as <code>BusinessObject</code> or <code>IList</code>), <code>value</code> must <em>not</em>
		/// be a <code>String</code>.  For example, if the <code>PrimaryAddress</code> field
		/// of <code>this</code> <code>BusinessObject</code> is supposed to be of type
		/// <code>Address</code> (that is, if the XML that defines the <code>PrimaryAddress</code>
		/// field of <code>this</code> <code>BusinessObject</code> says <code>type="Address"</code>),
		/// then (assuming <code>Address</code> is the <code>type()</code> of some <code>BusinessObject</code>),
		/// it is illegal to say <code>this.put("PrimaryAddress","aString")</code>.</li>
		/// 
		/// <li>if the type of <code>value</code> is supposed to be some kind of
		/// <code>BusinessObject</code>, <code>value</code> must be a <code>BusinessObject</code>
		/// of the right type.  For example, if the <code>PrimaryAddress</code> field
		/// of <code>this</code> <code>BusinessObject</code> is supposed to be of type
		/// <code>Address</code> (assuming <code>Address</code> is the <code>type()</code> of some
		/// <code>BusinessObject</code>), then <code>((BusinessObject)value).type()</code> must
		/// return <code>"Address"</code>.</li>
		/// </ul>
		/// </p><p>
		/// <b>Postconditions:</b> <em>Important:</em> unlike normal postconditions,
		/// these postconditions will be provided even if the "deferred preconditions"
		/// are <em>not</em> met:
		/// <ul>
		/// <li><code>this.get(name)</code> will return <code>value</code>.</li>
		/// <li><code>this.isDirty()</code> will return <code>true</code>.</li>
		/// <li><code>this.dirtyFields().contains(name)</code> will return <code>true</code>.</li>
		/// </ul>
		/// </p></remarks>
		/// <param name="name">The name of the field whose value is being changed.  Must be
		/// valid (that is, <code>this.containsField(name)</code> must return <code>true</code>).
		/// If not, throws a <code>BadField</code> exception.</param>
		/// <param name="newValue">The new value for the field.</param>
		void put(string name, object newValue);
		void put(string nameAddress, object val, object extraInfo);

		bool isGetOnly{get;}
		XmlSerializationSupport SerializationSupport {get;}
		string Xml{get;}
		string getXml(string options);
		bool containsField(String nameAddress);
	}
}